import time
import static
import json
import copy
import re
import mss
import time
import concurrent.futures
from pathlib import Path
from winreg import *

# Local imports
import ocr
import recognition
import simulatedinput
import monitor
import gameplan
from logger import logger as log


# Definently fix this
class Bot:
    def __init__(
        self,
        instruction_path,
        debug_mode=False,
        restart_mode=False,
        sandbox_mode=False,
        game_plan_filename="instructions.json",
        game_settings_filename="setup.json",
    ):
        is_url_regex = re.compile(
            r"https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)"
        )
        if re.search(is_url_regex, str(instruction_path)):
            self.settings = gameplan.load_from_url(instruction_path)
            self.game_plan = gameplan.load_from_url(instruction_path)
        else:
            self.settings = gameplan.load_from_file(
                instruction_path / game_settings_filename
            )
            self.game_plan = gameplan.load_from_file(
                instruction_path / game_plan_filename
            )

        # 深度复制game_plan属性到_self._game_plan_copy，以确保两个对象独立且不共享状态
        self._game_plan_copy = copy.deepcopy(self.game_plan)
        self.round_area = None
        self.DEBUG = debug_mode
        self.RESTART = restart_mode
        self.SANDBOX = sandbox_mode

        if self.SANDBOX and self.RESTART:
            raise Exception(
                "Sandbox mode and restart mode cannot be used at the same time"
            )

        self.start_time = time.time()
        self.game_start_time = time.time()

        self.running = True
        self.fast_forward = True

        self.statDict = {
            "Current_Round": None,
            "Last_Upgraded": None,
            "Last_Target_Change": None,
            "Last_Placement": None,
            "Uptime": 0,
        }

    def handle_time(self, ttime):
        """
        将秒转换为适当的单位。

        参数:
        ttime (int): 需要转换的秒数。

        返回:
        tuple: 包含两个元素的元组，第一个元素是转换后的数值，第二个元素是单位（'s', 'min', 'hrs', 'd', 'w'）。
        """
        if ttime >= 60:  # 转换为分钟
            return (ttime / 60, "min")
        elif (ttime / 60) >= 60:  # 转换为小时
            return (ttime / 3600, "hrs")
        elif (ttime / 3600) >= 24:  # 转换为天
            return (ttime / 86400, "d")
        elif (ttime / 86400) >= 7:  # 转换为周
            return (ttime / 604800, "w")
        else:  # 默认为秒
            return (ttime, "s")

    # Put this somewhere else
    def log_stats(self, did_win: bool = None, match_time: int | float = 0):
        # Standard dict which will be used if json loads nothing
        data = {
            "wins": 0,
            "loses": 0,
            "winrate": "0%",
            "average_matchtime": "0 s",
            "total_time": 0,
            "average_matchtime_seconds": 0,
        }

        # Try to read the file
        try:
            with open("stats.json", "r") as infile:
                try:
                    # Read json file
                    str_file = "".join(infile.readlines())
                    data = json.loads(str_file)
                # Catch if file format is invalid for json (eg empty file)
                except json.decoder.JSONDecodeError:
                    log.error("invalid stats file while logging stats")
        # Catch if the file does not exist
        except IOError:
            log.error("stats file does not exist")

        if did_win:
            data["wins"] += 1
        else:
            data["loses"] += 1

        total_matches = data["wins"] + data["loses"]
        # winrate = total wins / total matches
        winrate = data["wins"] / total_matches

        # Convert to procent
        procentage = round(winrate * 100, 4)

        # Push procentage to winrate
        data["winrate"] = f"{procentage}%"

        data["average_matchtime_seconds"] = (
            data["total_time"] + match_time
        ) / total_matches

        # new_total_time = old_total_time + current_match_time in seconds
        data["total_time"] += match_time

        # average = total_time / total_matches_played
        average_converted, unit = self.handle_time(data["average_matchtime_seconds"])

        # Push average to dictionary
        data["average_matchtime"] = f"{round(average_converted, 3)} {unit}"

        # Open as write
        with open("stats.json", "w") as outfile:
            outfile.write(json.dumps(data, indent=4))  # write stats to file

        return data

    def reset_game_plan(self):
        self.game_plan = copy.deepcopy(self._game_plan_copy)

    def initilize(self):
        if self.DEBUG:
            log.debug("RUNNING IN DEBUG MODE, DEBUG FILES WILL BE GENERATED")
        simulatedinput.send_key("alt")

    def loop(self):
        if self.SANDBOX:
            print("Sandbox mode started")
            for instructionGroup in self.game_plan.keys():
                for instruction in self.game_plan.get(instructionGroup):
                    self.execute_instruction(instruction)
            print("Sandbox mode finished")

            self.running = False
            return

        current_round = -1
        ability_one_timer = time.time()
        ability_two_timer = time.time()
        ability_three_timer = time.time()

        finished = False

        middle_of_screen = (0.5, 0.5)

        # main ingame loop
        while not finished:
            # Check for levelup or insta monkey (level 100)
            if self.checkFor(["levelup", "levelup_1", "instamonkey"]):
                print("Levelup detected")
                log.info("Levelup detected")
                simulatedinput.click(middle_of_screen, amount=3)
            elif self.checkFor("monkey_knowledge"):
                print("Monkey knowledge detected")
                log.info("Monkey knowledge detected")
                simulatedinput.click(middle_of_screen, amount=1)
            # Check for finished or failed game
            did_win, did_fail = self.checkFor(["victory", "defeat"], return_raw=True)
            if did_win or did_fail:
                if did_win:
                    print("We won")
                    log.info("Victory detected; exiting level")
                else:
                    print("We lost")
                    log.info(
                        "Defeat detected on round {}; exiting level".format(
                            current_round
                        )
                    )

                win_or_lose = True if did_win else False  # is this correct logic?
                print(win_or_lose)
                self.log_stats(
                    did_win=win_or_lose, match_time=(time.time() - self.game_start_time)
                )

                if self.RESTART:
                    self.restart_level(won=win_or_lose)
                else:
                    self.exit_level(won=win_or_lose)

                finished = True
                self.reset_game_plan()
                break

            current_round = self.getRound()

            if current_round != None:
                # Saftey net; use abilites
                # TODO: Calculate round dynamically, based on which round hero has been placed.
                if self.settings["HERO"] != "GERALDO":  # geraldo doesn't any ability
                    cooldowns = static.hero_cooldowns[self.settings["HERO"]]

                    if current_round >= 7 and self.abilityAvaliabe(
                        ability_one_timer, cooldowns[0]
                    ):
                        simulatedinput.send_key("1")
                        ability_one_timer = time.time()

                    # skip if ezili or adora, their lvl 7 ability is useless
                    if (
                        current_round >= 31
                        and self.abilityAvaliabe(ability_two_timer, cooldowns[1])
                        and (self.settings["HERO"] != "EZILI" and "ADORA")
                    ):
                        simulatedinput.send_key("2")
                        ability_two_timer = time.time()

                    if len(cooldowns) == 3:
                        if current_round >= 53 and self.abilityAvaliabe(
                            ability_three_timer, cooldowns[2]
                        ):
                            simulatedinput.send_key("3")
                            ability_three_timer = time.time()

                # Is this necessary?
                # Check for round in game plan
                if str(current_round) in self.game_plan:

                    # Handle all instructions in current round
                    for instruction in self.game_plan[str(current_round)]:
                        if not "DONE" in instruction:
                            self.execute_instruction(instruction)
                            instruction["DONE"] = True
                            log.debug(
                                f"Current round {current_round}"
                            )  # Only print current round once

    def exit_bot(self):
        self.running = False

    def place_tower(self, tower_position, keybind):
        simulatedinput.send_key(keybind)  # press keybind
        simulatedinput.click(tower_position)  # click on decired location

    def upgrade_tower(self, tower_position, upgrade_path):
        if (
            not any(isinstance(path, int) for path in upgrade_path)
            or len(upgrade_path) != 3
        ):
            raise Exception("Upgrade path must be a list of integers", upgrade_path)

        simulatedinput.click(tower_position)

        # Convert upgrade_path to something usable
        top, middle, bottom = upgrade_path

        for _ in range(top):
            simulatedinput.send_key("top")

        for _ in range(middle):
            simulatedinput.send_key("middle")

        for _ in range(bottom):
            simulatedinput.send_key("bottom")

        simulatedinput.send_key("esc")

    def change_target(
        self,
        tower_type,
        tower_position,
        targets: list[str] | str,
        delay: int | float | list | tuple = 3,
    ):
        if not isinstance(targets, (tuple, list)):
            targets = [targets]

        if isinstance(targets, (list, tuple)) and isinstance(delay, (tuple, list)):
            # check if delay and targets are the same length
            if len(targets) != len(delay):
                raise Exception(
                    "Number of targets and number of delays needs to be the same"
                )

        simulatedinput.click(tower_position)

        if "SPIKE" in tower_type:
            target_order = static.target_order_spike
        else:
            target_order = static.target_order_regular

        current_target_index = 0

        # for each target in target list
        for i in targets:

            while current_target_index != target_order.index(i):
                simulatedinput.send_key("tab")
                current_target_index += 1
                if current_target_index > 3:
                    current_target_index = 0

            # If delay is an int sleep for delay for each target
            if isinstance(delay, (int, float)):
                # If the bot is on the last target  in targets list, dont sleep
                if targets[-1] != i:  #
                    time.sleep(delay)
            # If delay is a list sleep for respective delay for each target
            elif isinstance(delay, (list, tuple)):
                time.sleep(delay.pop(-1))

        simulatedinput.send_key("esc")

    def set_static_target(self, tower_position, target_pos):
        simulatedinput.click(tower_position)

        simulatedinput.send_key("tab")

        simulatedinput.click(target_pos)

        simulatedinput.send_key("esc")

    def remove_tower(self, position):
        simulatedinput.click(position)
        simulatedinput.send_key("backspace")
        simulatedinput.send_key("esc")

    def execute_instruction(self, instruction):
        """Handles instructions from the gameplan"""

        instruction_type = instruction["INSTRUCTION_TYPE"]

        if instruction_type == "PLACE_TOWER":
            tower = instruction["ARGUMENTS"]["MONKEY"]
            position = instruction["ARGUMENTS"]["LOCATION"]

            keybind = static.tower_keybinds[tower]

            self.place_tower(position, keybind)

            log.debug(f"Tower placed: {tower}")

        elif instruction_type == "REMOVE_TOWER":
            self.remove_tower(instruction["ARGUMENTS"]["LOCATION"])
            log.debug(f"Tower removed on: {instruction['ARGUMENTS']['LOCATION']}")

        # Upgrade tower
        elif instruction_type == "UPGRADE_TOWER":
            position = instruction["ARGUMENTS"]["LOCATION"]
            upgrade_path = instruction["ARGUMENTS"]["UPGRADE_PATH"]

            self.upgrade_tower(position, upgrade_path)

            log.debug(
                f"Tower upgraded at position: {instruction['ARGUMENTS']['LOCATION']} with the upgrade path {instruction['ARGUMENTS']['UPGRADE_PATH']}"
            )

        # Change tower target
        elif instruction_type == "CHANGE_TARGET":
            target_type = instruction["ARGUMENTS"]["TYPE"]
            position = instruction["ARGUMENTS"]["LOCATION"]
            target = instruction["ARGUMENTS"]["TARGET"]

            if "DELAY" in instruction["ARGUMENTS"]:
                delay = instruction["ARGUMENTS"]["DELAY"]
                self.change_target(target_type, position, target, delay)
            else:
                self.change_target(target_type, position, target)

        # Set static target of a tower
        elif instruction_type == "SET_STATIC_TARGET":
            position = instruction["ARGUMENTS"]["LOCATION"]
            target_position = instruction["ARGUMENTS"]["TARGET"]

            self.set_static_target(position, target_position)

        # Start game
        elif instruction_type == "START":
            if (
                "ARGUMENTS" in instruction
                and "FAST_FORWARD " in instruction["ARGUMENTS"]
            ):
                self.fast_forward = instruction["ARGUMENTS"]["FASTFORWARD"]

            self.start_first_round()

            log.debug("First Round Started")

        # Wait a given time
        elif instruction_type == "WAIT":
            time.sleep(instruction["ARGUMENTS"]["TIME"])

            log.debug(f"Waiting for {instruction['ARGUMENTS']['TIME']} second(s)")

        else:
            # Maybe raise exception or just ignore?
            raise Exception(
                "Instruction type {} is not a valid type".format(instruction_type)
            )

        log.debug(f"executed instruction:\n{instruction}")

    def abilityAvaliabe(self, last_used, cooldown):
        # TODO: Store if the game is speeded up or not. If it is use the constant (true by default)
        m = 1

        if self.fast_forward:
            m = 3

        return (time.time() - last_used) >= (cooldown / m)

    def start_first_round(self):
        if self.fast_forward:
            simulatedinput.send_key("space", amount=2)
        else:
            simulatedinput.send_key("space", amount=1)

        self.game_start_time = time.time()

    def check_for_collection_crates(self):
        # Can this be done better?
        if self.checkFor("diamond_case"):
            log.debug("easter collection detected")

            simulatedinput.click("EASTER_COLLECTION")  # DUE TO EASTER EVENT:
            time.sleep(1)
            simulatedinput.click("LEFT_INSTA")  # unlock insta
            time.sleep(1)
            simulatedinput.click("LEFT_INSTA")  # collect insta
            time.sleep(1)
            simulatedinput.click("RIGHT_INSTA")  # unlock r insta
            time.sleep(1)
            simulatedinput.click("RIGHT_INSTA")  # collect r insta
            time.sleep(1)
            simulatedinput.click("F_LEFT_INSTA")
            time.sleep(1)
            simulatedinput.click("F_LEFT_INSTA")
            time.sleep(1)
            simulatedinput.click("MID_INSTA")  # unlock insta
            time.sleep(1)
            simulatedinput.click("MID_INSTA")  # collect insta
            time.sleep(1)
            simulatedinput.click("F_RIGHT_INSTA")
            time.sleep(1)
            simulatedinput.click("F_RIGHT_INSTA")
            time.sleep(1)

            time.sleep(1)
            simulatedinput.click("EASTER_CONTINUE")

            simulatedinput.send_key("esc")

    # select hero if not selected
    def hero_select(self):
        hero_vaiants = [f"{self.settings['HERO']}_{i}" for i in range(1, 4)]

        if not self.checkFor(hero_vaiants):
            log.debug(f"Selecting {self.settings['HERO']}")

            simulatedinput.click("HERO_SELECT")
            simulatedinput.click(
                static.hero_positions[self.settings["HERO"]], move_timeout=0.2
            )
            simulatedinput.click("CONFIRM_HERO")
            simulatedinput.send_key("esc")

    def exit_level(self, won=True):
        if won:
            simulatedinput.click("VICTORY_CONTINUE")
            time.sleep(2)
            simulatedinput.click("VICTORY_HOME")
        # Some Chimps and Deflation doesn't have continue button
        elif (
            self.settings["GAMEMODE"] == "CHIMPS_MODE"
            or self.settings["GAMEMODE"] == "DEFLATION"
        ):
            simulatedinput.click("DEFEAT_HOME_NO_CONTINUE")
            time.sleep(2)
        else:
            simulatedinput.click("DEFEAT_HOME")
            time.sleep(2)

        self.wait_for_loading()  # wait for loading screen

    def restart_level(self, won=True):
        if won:
            simulatedinput.click("VICTORY_CONTINUE")
            time.sleep(2)
            simulatedinput.click("FREEPLAY")
            # simulatedinput.click("OK_MIDDLE")
            simulatedinput.send_key("esc")

            time.sleep(2)
            simulatedinput.send_key("esc")
            time.sleep(1)
            simulatedinput.click("RESTART_WIN")
            simulatedinput.click("RESTART_CONFIRM")
        elif (
            self.settings["GAMEMODE"] == "CHIMPS_MODE"
            or self.settings["GAMEMODE"] == "DEFLATION"
        ):
            simulatedinput.click("RESTART_DEFEAT_NO_CONTINUE")
            simulatedinput.click("RESTART_CONFIRM")
            time.sleep(2)
        else:
            simulatedinput.click("RESTART_DEFEAT")
            simulatedinput.click("RESTART_CONFIRM")
            time.sleep(2)

        self.wait_for_loading()  # wait for loading screen

    def select_map(self):
        """
        选择游戏地图的函数。
        该函数根据用户设置选择对应的地图，自动完成在游戏菜单中的地图选择流程。
        无参数和返回值。
        """

        # 根据设置选择地图页和地图索引
        map_page = static.maps[self.settings["MAP"]][0]
        map_index = static.maps[self.settings["MAP"]][1]

        time.sleep(1)  # 等待界面响应

        # 点击开始游戏菜单和专家选择菜单
        simulatedinput.click("HOME_MENU_START")
        simulatedinput.click("EXPERT_SELECTION", timeout=0.25)

        # 进入初级选项页面
        simulatedinput.click("BEGINNER_SELECTION")

        # 根据map_page点击翻页,map_page>0点击右箭键,<0点击左箭键
        if  map_page > 0:
            simulatedinput.click(
                "RIGHT_ARROW_SELECTION", amount=(map_page - 1), timeout=0.2
            )
        else:
            simulatedinput.click(
                "LEFT_ARROW_SELECTION", amount=(abs(map_page)), timeout=0.2
            )
        # 点击目标地图
        simulatedinput.click("MAP_INDEX_" + str(map_index))

        # 根据是否为沙盒模式，选择难度和游戏模式
        if self.SANDBOX:
            simulatedinput.click("EASY_MODE")
            simulatedinput.click("SANDBOX_EASY")
        else:
            simulatedinput.click(self.settings["DIFFICULTY"])
            simulatedinput.click(self.settings["GAMEMODE"])

            # 覆盖保存
            simulatedinput.click("OVERWRITE_SAVE")

        # 等待加载画面
        self.wait_for_loading()

        # 如果游戏模式需要确认，按下确认键
        time.sleep(1)
        confirm_list = [
            "CHIMPS_MODE",
            "IMPOPPABLE",
            "DEFLATION",
            "APOPALYPSE",
            "HALF_CASH",
        ]
        if self.settings["GAMEMODE"] in confirm_list or self.SANDBOX:
            simulatedinput.send_key("esc", timeout=1)

    def wait_for_loading(self):
        """
        等待加载屏幕消失。

        此函数不接受参数，也不返回任何值。
        主要通过循环检查是否仍然处于加载屏幕，一旦加载屏幕消失，则停止等待。
        """
        still_loading = True  # 设定初始状态为正在加载

        log.debug("Waiting for loading screen..")  # 记录调试信息，表示开始等待加载屏幕
        while still_loading:
            time.sleep(0.5)  # 每0.2秒检查一次，避免过度消耗CPU资源
            still_loading = self.checkFor("loading_screen")  # 检查是否仍然处于加载屏幕

        log.debug(
            "Out of loading screen, continuing.."
        )  # 加载屏幕消失，记录调试信息，继续执行后续操作

    def getRoundArea(self):
        """
        获取回合区域的面积信息。

        该方法首先尝试寻找屏幕上的回合文本，并据此确定回合区域的位置和大小。
        如果找到了回合区域，则返回带有偏移量调整后的回合区域的尺寸和位置；
        如果未找到，则返回默认的回合区域尺寸和位置。

        返回值:
            dict: 包含回合区域的宽度、高度以及计算得到的顶部和左侧偏移量。
        """
        # 初始化回合区域的尺寸
        round_area = {"width": 200, "height": 42}

        # 寻找屏幕上的“回合”文本，并获取其坐标位置
        area = self.checkFor("round", return_cords=True, center_on_found=False)
        log.debug("this should be only printed once, getting round area")
        log.debug(f"Round area found at {area}, applying offsetts")

        if area:
            log.info("Found round area!")

            # 根据找到的文本位置，计算回合区域的顶部和左侧偏移量
            x, y, roundwidth, roundheight = area

            # 定义文本到回合数字的偏移量
            xOffset = roundwidth + 10
            yOffset = int(roundheight * 3) - 40

            # 应用偏移量调整回合区域的位置
            round_area["top"] = y + yOffset
            round_area["left"] = x - xOffset

            return round_area

        # 如果未找到回合文本，使用默认的偏移量设置
        log.warning("Could not find round area, setting default values")
        default_round_area_scaled = monitor.scaling(
            [0.7083333333333333, 0.0277777777777778]
        )  # 使用默认尺寸，基于1080p分辨率的(1360,30)

        # 设置默认的回合区域位置
        round_area["left"] = default_round_area_scaled[0]
        round_area["top"] = default_round_area_scaled[1]
        return round_area

    def getRound(self):
        """
        获取当前回合数。
        该方法首先检查是否已经定位到回合区域，如果未定位则调用 getRoundArea 方法进行定位。
        定位后，截取回合数所在屏幕区域，并通过 OCR 技术识别出回合数，最后返回识别出的回合数。

        返回:
            int: 识别到的当前回合数。如果无法识别或发生错误，则返回 None。
        """
        # 检查是否已经定位到回合区域，未定位则调用 getRoundArea 方法
        if self.round_area is None:
            self.round_area = self.getRoundArea()

        # 设置屏幕截取区域
        screenshot_dimensions = {
            "top": self.round_area["top"],
            "left": self.round_area["left"],
            "width": self.round_area["width"],
            "height": self.round_area["height"] + 50,  # 额外增加高度以确保截取完整
        }

        # 截取屏幕并识别文本
        with mss.mss() as screenshotter:
            screenshot = screenshotter.grab(screenshot_dimensions)
            found_text, _ocrImage = ocr.getTextFromImage(screenshot)

            # 若处于调试模式，保存 OCR 识别出的图像
            if self.DEBUG:
                from cv2 import imwrite, IMWRITE_PNG_COMPRESSION

                def get_valid_filename(s):
                    # 生成合法的文件名
                    s = str(s).strip().replace(" ", "_")
                    return re.sub(r"(?u)[^-\w.]", "", s)

                imwrite(
                    f"./DEBUG/OCR_DONE_FOUND_{get_valid_filename(found_text)}_{str(time.time())}.png",
                    _ocrImage,
                    [IMWRITE_PNG_COMPRESSION, 0],
                )

            # 从识别的文本中提取并返回回合数
            if re.search(r"(\d+/\d+)", found_text):
                found_text = re.search(r"(\d+)", found_text)  # 只取第一个数字组
                return int(found_text.group(0))
            # 特殊情况处理：识别结果为 "71180" 时返回 77
            elif found_text == "71180":
                return 77
            else:
                # 如果识别的文本不符合要求，记录警告并保存当前截取的图像
                log.warning(
                    "Found text '{}' does not match regex requirements".format(
                        found_text
                    )
                )

                try:
                    file_path = Path(__file__).resolve().parent.parent / "DEBUG"
                    if not file_path.exists():
                        Path.mkdir(file_path)  # 创建 DEBUG 文件夹

                    with open(
                        file_path / f"GETROUND_IMAGE_{str(time.time())}.png", "wb"
                    ) as output_file:
                        output_file.write(
                            mss.tools.to_png(screenshot.rgb, screenshot.size)
                        )

                    log.warning("Saved screenshot of what was found")

                except Exception as e:
                    log.error(e)
                    log.warning("Could not save screenshot of what was found")

                return None  # 无法识别或发生错误时返回 None

    def waitForRound(self, round) -> None:
        """Wait for a specific round to start, use this to wait until to execute a gameplan instruction"""
        pass

    def waitFor(
        self, images: list[str] | str, confidence: float = 0.9, timeout: int = 10
    ) -> None:
        """Wait for a image to appear on screen, generic function for multiple things"""
        pass

    def checkFor(
        self,
        images: list[str] | str,
        confidence: float = 0.9,
        return_cords: bool = False,
        center_on_found: bool = True,
        return_raw: bool = False,
    ) -> bool:
        """
        通用函数，用于检查屏幕上是否存在指定图像。

        参数:
        - images: 图像列表或单个图像路径。如果是列表，每个元素都是要查找的图像的名称。
        - confidence: 识别置信度，默认为0.9，即只有当置信度高于此值时才认为图像被找到。
        - return_cords: 是否返回图像在屏幕上的坐标，默认为False。如果为True，则在找到图像时返回其坐标。
        - center_on_found: 当图像被找到时，是否将屏幕中心移动到该图像上，默认为True。
        - return_raw: 是否返回原始识别结果，默认为False。如果为True，则返回包含所有查找结果的列表。

        返回值:
        - 如果设置了return_raw为True，则返回一个列表，包含每个图像的查找结果（找到为True，未找到为False）。
        - 如果未设置return_raw为True，并且至少找到一个图像，则返回True；如果未找到任何图像，则返回False。
        """

        # 确定资源（图像）目录的路径
        assets_directory = Path(__file__).resolve().parent / "assets"
        # 定义一个函数，用于获取给定图像的完整路径
        image_path = lambda image: assets_directory / f"{image}.png"
        if isinstance(images, list):

            output = [None] * len(images)  # 初始化输出列表

            # 使用并发执行器来并行处理图像识别任务
            with concurrent.futures.ThreadPoolExecutor() as executor:
                # 为每个图像提交一个识别任务到线程池，并记录任务与对应图像的索引关系
                futures = {
                    executor.submit(recognition.find, image_path(image)): idx
                    for idx, image in enumerate(images)
                }

                # 等待所有任务完成，并收集结果
                for future in concurrent.futures.as_completed(futures):
                    output[futures[future]] = future.result()

            # 如果需要返回原始结果，则直接返回输出列表
            if return_raw:
                return output

            # 如果只需要知道是否有图像被找到，则返回结果列表中任意元素为True即可
            return any(output)
        else:
            # 如果输入是单个图像，则直接调用识别函数，并根据需要处理返回值
            return recognition.find(
                image_path(images), confidence, return_cords, center_on_found
            )
            
    def findStore(self):
        """
        检查游戏是安装在Steam上还是Epic Games上。
        
        无参数。
        
        返回值:
        - game_on_Steam: 布尔值，表示游戏是否安装在Steam上。
        - game_on_EpicGames: 布尔值，表示游戏是否安装在Epic Games上。
        """
        
        # 检查游戏是否安装在Steam上
        reg = ConnectRegistry(None,HKEY_CURRENT_USER)
        game_on_Steam = False
        try:
            key = OpenKey(reg, r"SOFTWARE\Valve\Steam\Apps\960090")
            if key:
                try:
                    isInstalled = QueryValueEx(key, "Installed")[0]
                    # 其他有用的值包括"Running"和"Updating"
                    if isInstalled == 1:
                        game_on_Steam = True
                        print("检测到Steam安装游戏。")
                except WindowsError:
                    # 游戏未安装但可能在Steam库中
                    pass
            CloseKey(key)
        except WindowsError:
            # 游戏未安装在Steam上
            pass
        CloseKey(reg)

        # 如果游戏未安装在Steam上，检查Epic Games
        reg = ConnectRegistry(None,HKEY_LOCAL_MACHINE)
        game_on_EpicGames = False
        try:
            key = OpenKey(reg, r"SOFTWARE\WOW6432Node\Epic Games\EpicGamesLauncher")
            if key:
                try:
                    epicgamesLauncherPath = QueryValueEx(key, "AppDataPath")[0]
                    if epicgamesLauncherPath:
                        path = re.search(r".+Epic\\", epicgamesLauncherPath).group(0) + "UnrealEngineLauncher\LauncherInstalled.dat"
                        try:
                            with open(path) as f:
                                data = json.load(f)
                            for x in data["InstallationList"]:
                                if x["NamespaceId"] == "6a8dfa6e441e4f2f9048a98776c6077d":
                                    game_on_EpicGames = True
                                    print("检测到Epic Games安装游戏。")
                        except:
                            # 游戏未安装在Epic Games
                            pass
                except WindowsError:
                    # 无法找到Epic Games启动器路径
                    pass
            CloseKey(key)
        except WindowsError:
            # Epic Games启动器未安装
            pass
        CloseKey(reg)
        
        return(game_on_Steam,game_on_EpicGames)


if __name__ == "__main__":
    import time

    time.sleep(2)
    bot = Bot(instruction_path="")
    print(bot.getRound())
